#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <cs104_slave.h>
#include "hal_thread.h"
#include "hal_time.h"
#include "z_logger.h"
#include "sqlite.h"
#include "redis.h"
#include "sched_pub.h"
#include "slave_handler.h"
#include "asdu_handler.h"
#include "channel.h"

list_t *connections = NULL;

bool connection_request_handler(void *parameter, const char *ip_addr) {
    log_info("New connection request from %s", ip_addr);

    return true;
}

void connection_event_handler(void *parameter, IMasterConnection connection, CS104_PeerConnectionEvent event) {
    const char *event_strings[] = {"opened", "closed", "activated", "deactivated"};
    log_info("Connection %s (%p)", event_strings[event], connection);

    switch (event) {
        case CS104_CON_EVENT_ACTIVATED:
            list_add(connections, connection);
            seqiot_t *seqiot = parameter;
            //断开后的每一次重新连接，将判断位重置为1
            seqiot->default_uplink->times = 1;
            break;
        case CS104_CON_EVENT_DEACTIVATED:
            list_del(connections, connection);
            break;
        default:
            break;
    }
}

void raw_message_handler(void *parameter, IMasterConnection connection, uint8_t *msg, int size, bool sent) {
    const unsigned int max_size = 256;
    const char *sent_strings[] = {"recv", "send",};
    const char *hex_dict = "0123456789abcdef";

    char hex[max_size * 2 + 1];
    for (int i = 0; i < size && i < max_size; ++i) {
        hex[i * 2] = hex_dict[msg[i] / 0x10];
        hex[i * 2 + 1] = hex_dict[msg[i] % 0x10];
    }
    hex[(size > max_size ? max_size : size) * 2] = '\0';

    log_trace("%s %d bytes: %s", sent_strings[sent], size, hex);
}

bool clock_sync_handler(void *parameter, IMasterConnection connection, CS101_ASDU asdu, CP56Time2a time) {
    uint64_t timestamp = CP56Time2a_toMsTimestamp(time);
    log_info("Process time sync command with timestamp %d", timestamp);

    CP56Time2a_setFromMsTimestamp(time, Hal_getTimeInMs());
    /* update system time here */

    return true;
}

unsigned int background_scan_handler(IMasterConnection connection, pub_handler_t *pub_handler) {
    pub_handler->handle = connection;
    iec104_params_t *args = pub_handler->args;
    seqiot_t *seqiot = args->seqiot;
    //初次连接才传送初始化结束消息
    if (1 == seqiot->default_uplink->times) {
        CS101_AppLayerParameters app_layer_params = IMasterConnection_getApplicationLayerParameters(
                connection);
        CS101_ASDU asdu = CS101_ASDU_create(
                app_layer_params,
                false,
                CS101_COT_INITIALIZED,
                0,
                seqiot->default_uplink->rtu,
                false,
                false);
        InformationObject io;
        io = (InformationObject) MeasuredValueShort_create(
                NULL,
                0,
                0,
                IEC60870_QUALITY_GOOD);
        CS101_ASDU_addInformationObject(asdu, io);
        InformationObject_destroy(io);
        CS101_ASDU_setTypeID(asdu, 70);
        IMasterConnection_sendASDU(connection, asdu);
        seqiot->default_uplink->times++;
    }
    //遥脉，每300秒检查一次
    if (args->sched_time % 300 == 0) {
        pub_handler->on_filter = totals_filter;
        check_next_sched(pub_handler, args->seqiot, args->sched_time);
        publish_changed(pub_handler, args->seqiot);
    }

    //遥测，每60秒检查一次
    if (args->sched_time % 60 == 0) {
        pub_handler->on_filter = analog_filter;
        check_next_sched(pub_handler, args->seqiot, args->sched_time);
        publish_changed(pub_handler, args->seqiot);
    }

    //遥信，每5秒检查一次
    if (args->sched_time % 5 == 0) {
        pub_handler->on_filter = digital_filter;
        check_next_sched(pub_handler, args->seqiot, args->sched_time);
        publish_changed(pub_handler, args->seqiot);
    }

}

int main(int argc, char **argv) {
    log_init();
    seqiot_t *seqiot = seqiot_new();

    if (0 != read_config(seqiot, NULL, 0, 0)) {
        return EXIT_FAILURE;
    }
    if (!redis_open(seqiot->local_address)) {
        return EXIT_FAILURE;
    }

    connections = list_new(10);
    CS104_Slave slave = CS104_Slave_create(10, 10);
    CS104_Slave_setLocalAddress(slave, "0.0.0.0");
    CS104_Slave_setLocalPort(slave, 2404);
    CS104_Slave_setServerMode(slave, CS104_MODE_SINGLE_REDUNDANCY_GROUP);

    CS104_APCIParameters conn_params = CS104_Slave_getConnectionParameters(slave);
    int heartbeat = (int) seqiot->default_uplink->heartbeat;
    conn_params->t0 *= heartbeat;
    conn_params->t1 *= heartbeat;
    conn_params->t2 *= heartbeat;

    CS101_AppLayerParameters app_params = CS104_Slave_getAppLayerParameters(slave);

    CS104_Slave_setConnectionRequestHandler(slave, connection_request_handler, seqiot);
    CS104_Slave_setConnectionEventHandler(slave, connection_event_handler, seqiot);
    CS104_Slave_setRawMessageHandler(slave, raw_message_handler, seqiot);
    CS104_Slave_setASDUHandler(slave, asdu_handler, seqiot);
    CS104_Slave_setClockSyncHandler(slave, clock_sync_handler, seqiot);
    CS104_Slave_setInterrogationHandler(slave, interrogation_handler, seqiot);
    CS104_Slave_setCounterInterrogationHandler(slave, counter_interrogation_handler, seqiot);


    struct timeval _tv;
    gettimeofday(&_tv, NULL);
    //必须调用一次__timezone才有值
    localtime(&_tv.tv_sec);

    for (int i = 0; i < vector_size(&seqiot->catalogs); ++i) {
        data_catalog_t *catalog = vector_get(&seqiot->catalogs, i);
        if (catalog->pub.period < 0) {
            catalog->_next_sched = _tv.tv_sec;
        } else {
            catalog->_next_sched = next_sched(_tv.tv_sec, &catalog->pub);
        }
    }

    iec104_params_t iec104_params = {
            .seqiot = seqiot,
            .sched_time = 0,
            .cot = CS101_COT_PERIODIC,
            .with_timestamp = false,
    };

    pub_handler_t pub_handler = {
            .handle = NULL,
            .args = &iec104_params,
            .on_filter = catalog_filter,
            .on_compare = value_changed_compare,
            .on_write = iec104_asdu_write,
            .on_flush = iec104_asdu_flush,
    };

    CS104_Slave_start(slave);
//    CS104_Slave_startThreadless(slave);
    if (CS104_Slave_isRunning(slave) == false) {
        log_warn("Starting server failed!");
        goto err;
    }

    while (running) {
        struct timeval _tv;
        gettimeofday(&_tv, NULL);
        iec104_params.sched_time = _tv.tv_sec;

        list_foreach(connections, (list_func_t) background_scan_handler, &pub_handler);

//        CS104_Slave_tick(slave);
        Thread_sleep(1000);
    }

    CS104_Slave_stop(slave);
//    CS104_Slave_stopThreadless(slave);

    err:
    CS104_Slave_destroy(slave);
    list_delete(connections);

    redis_close();
    seqiot_free(seqiot);
    log_fini();

    return EXIT_SUCCESS;
}